package cn.jtfadmin.base.lang.common.utils;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 动态编译工具
 * @author jtf
 * @since 0.0.2
 */
public class DynamicCompilerUtils {

    private static String springSourceDir;


    public static Class<?> compilerAndLoad(ClassLoader parentClassLoad, String className, String source, String... extClasspath) {
        File javaIoTmpDir = SystemUtils.getJavaIoTmpDir();
        File targetDir = new File(javaIoTmpDir, RandomStringUtils.random(8, true, false));
        try {
            compilerToFile(className, source, targetDir, extClasspath);
            return new URLClassLoader(new URL[]{targetDir.toURI().toURL()}, parentClassLoad).loadClass(className);
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new RuntimeException(e);
        } finally {
            FileSystemUtils.deleteRecursively(targetDir);
        }
    }

    public static File compilerToFile(String className, String source, File targetDir, String... extClasspath) {


        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
        if (javaCompiler == null) {
            throw new RuntimeException("jdk 未提供动态编译工具");
        }
        File javaIoTmpDir = SystemUtils.getJavaIoTmpDir();

        File sourceDir = new File(javaIoTmpDir, RandomStringUtils.random(8, true, false));

        File libDir = StringUtils.hasText(springSourceDir) ? new File(springSourceDir):
                new File(javaIoTmpDir, RandomStringUtils.random(8, true, false));

        try (CharArrayWriter err = new CharArrayWriter()) {
            Path path = Paths.get(sourceDir.getAbsolutePath(), className.replace(".", "/") + ".java");
            Path dir = path.getParent();
            if (!Files.exists(dir)) {
                Files.createDirectories(dir);
            }
            try (ByteArrayInputStream inputStream = new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8))) {
                Files.copy(inputStream, path);
            }
            if (!targetDir.exists()) {
                targetDir.mkdirs();
            }

            StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, Locale.CHINESE, StandardCharsets.UTF_8);
            Iterable<? extends JavaFileObject> javaFileObjects = fileManager.getJavaFileObjects(path.toFile());
            List<String> options = new ArrayList<>();
            options.add("-target");
            options.add("1.8");
            options.add("-encoding");
            options.add("utf-8");
            options.add("-d");
            options.add(targetDir.getAbsolutePath());
            options.add("-classpath");

            Set<String> classPath = new HashSet<>();

            if (extClasspath != null) {
                for (String s : extClasspath) {
                    classPath.add(s);
                }
            }
            String sysClassPath = System.getProperty("java.class.path");
            if(sysClassPath.contains(File.pathSeparator)){
                // 通过ide运行的
                classPath.add(sysClassPath);
            }else {
                // 打包后的
                File springjar = new File(SystemUtils.getUserDir(), sysClassPath);

                if(!libDir.exists()){
                    CompressUtils.decompressJar(libDir, springjar);
                    sourceDir = libDir.getAbsoluteFile();
                }

                for (File file : new File(libDir, "BOOT-INF/lib").listFiles()) {
                    classPath.add(file.getAbsolutePath());
                }
                classPath.add(new File(libDir,"BOOT-INF/class").getAbsolutePath());

            }
            classPath.forEach(
                    t-> System.out.println(t)
            );
            options.add(StringUtils.collectionToDelimitedString(classPath, File.pathSeparator));

            JavaCompiler.CompilationTask task = javaCompiler.getTask(err, fileManager,
                    null, options,
                    null, javaFileObjects);
            Boolean call = task.call();
            if (call) {
                File file = Paths.get(targetDir.getAbsolutePath(), className.replace(".", "/") + ".class").toFile();
                return file;

            } else {
                throw new RuntimeException(new String(err.toCharArray()));
            }
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new RuntimeException(e);
        } finally {
            FileSystemUtils.deleteRecursively(sourceDir);
            FileSystemUtils.deleteRecursively(libDir);
        }

    }


    public static byte[] compilerToByte(String className, String source, String... extClasspath) {
        File javaIoTmpDir = SystemUtils.getJavaIoTmpDir();
        File targetDir = new File(javaIoTmpDir, RandomStringUtils.random(8, true, false));
        File file = compilerToFile(className, source, targetDir, extClasspath);
        try (InputStream is = new FileInputStream(file); ByteArrayOutputStream out = new ByteArrayOutputStream();) {
            StreamUtils.copy(is, out);
            return out.toByteArray();
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new RuntimeException(e);
        } finally {
            FileSystemUtils.deleteRecursively(targetDir);
        }
    }

}

//    public static void main(String[] args) {
//        try {
//            byte[] compiler = new DynamicCompilerUtils().compiler(
//                    DynamicCompilerUtils.class.getName(),
//                    FileCopyUtils.copyToString(new FileReader("/home/dell/项目/jtfadmin/java/base_lang/src/main/java/cn/jtfadmin/base/lang/common/utils/DynamicCompilerUtils.java")
//                    )
//            );
//            System.out.println(Arrays.toString(compiler));
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }

